package com.kgc.sbt.controller;

import com.alibaba.fastjson.JSON;
import com.kgc.sbt.beans.RushBFormat;
import com.kgc.sbt.service.RushBService;
import com.kgc.sbt.service.SearchService;
import com.kgc.sbt.utils.RequestResult;
import com.kgc.sbt.utils.ResultBuildUtil;
import com.kgc.sbt.utils.WxpayUtil;
import io.swagger.annotations.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * Created on 2021/8/19.
 * <p>
 * Author : Lcywings
 * <p>
 * Description :
 */
@RestController
@Slf4j
@Api(tags = " 3 抢购操作入口")
public class RushBController {

    @Autowired
    private RushBService rushBService;

    @Autowired
    private SearchService searchService;

    /**
     * @author : Lcywings
     * @date : 2021/8/12 11:21
     * @acl : true
     * @description : 开始抢购入口
     */
    @ApiImplicitParams({
            @ApiImplicitParam(name = "productCode", required = true, defaultValue = "0", value = "商品编号"),
            @ApiImplicitParam(name = "buyCount", required = true, defaultValue = "0", value = "购买数量")
    })
    @ApiResponses({
            @ApiResponse(code = 4002, message = "用户未登录"),
            @ApiResponse(code = 4003, message = "不存在的商品编号"),
            @ApiResponse(code = 5001, message = "库存数量失败或者是已经存在"),
            @ApiResponse(code = 5002, message = "抢购失败，同一个商品的抢购活动中，同一个用户只能抢购一次！"),
            @ApiResponse(code = 5003, message = "抢购失败，当前商品库存数量不足！"),
            @ApiResponse(code = 6033, message = "抢购失败！不在抢购时间，无法抢购时间。"),
            @ApiResponse(code = 5005, message = "抢购失败，请重新操作！")
    })
    @ApiOperation(value = "开始抢购入口", notes = "")
    @GetMapping(value = "/buyLimit")
    public RequestResult<RushBFormat> buyLimit(HttpServletRequest request, @RequestParam String productCode, @RequestParam Integer buyCount) throws Exception {
        // 校验数据
        Integer userId = getUserIdByToken(request);
        Integer prodId = getProductIdByproductCode(productCode);
        if (userId == -1) {
            return ResultBuildUtil.fail("4002", "用户未登录");
        }
        if (prodId == -1) {
            return ResultBuildUtil.fail("4003", "不存在的商品编号");
        }

        log.info("判断是否是抢购商品");
        if (!rushBService.isM2Product(prodId)) {
            return ResultBuildUtil.fail("6001", "预约失败！不是抢购商品。");
        }


        log.info("判断抢购商品是否在抢购时间");
        if (!rushBService.isM2ProductBuyTime2(prodId)) {
            return ResultBuildUtil.fail("6033", "抢购失败！不在抢购时间，无法抢购时间。");
        }


        log.info("------ 0 开始抢购,用户编号{},商品编号{},购买数量{} ------", userId, prodId, buyCount);

        // 模拟初始化商品库存数量到redis中，实际开发过程中,是后台初始化到Redis
        log.info("------ 1 初始化商品{}的库存数量 ------", prodId);
        if (!rushBService.initProdStorkToRedis(prodId)) {
            log.warn("------ 1 初始化商品：{}，库存数量失败或者是已经存在 ------", prodId);
            log.info("库存数量失败或者是已经存在");
        }

        // 锁定用户，抢购业务针对同一个用户，在当前商品整个抢购过程中，只能抢购一次
        log.warn("------ 2 校验当前抢购用户：{}，是否重复购买/是否成功抢购过改商品：{} ------", userId, prodId);
        if (!rushBService.lockBuyLimitUser(userId, prodId)) {
            log.warn("------ 2 同一个用户：{}，抢购商品{}，只能抢购一次 ------", userId, prodId);
            return ResultBuildUtil.fail("5002", "抢购失败，同一个商品的抢购活动中，同一个用户只能抢购一次！");
        }

        // 校验库存，个人库存不足，直接返回抢购失败
        log.warn("------ 3 校验当前抢购用户：{}，抢购商品：{}，库存数量是否充足 -------", prodId, buyCount);
        if (!rushBService.checkProdStock(prodId, buyCount)) {
            log.warn("------ 3 商品：{}，购买数量：{}，库存不足 -------", prodId, buyCount);
            return ResultBuildUtil.fail("5003", "抢购失败，当前商品库存数量不足！");
        }

        // 锁库存，如果当前锁库存操作失败，说明库存已经被其他用户锁住了，必须等待释放库存锁
        while (true) {
            // 循环判断库存锁是不是已经被释放，只有释放了库存锁，才可以进行后续流程操作（为了防止库存锁释放一次，必须给库存锁加过期时长)
            if (!rushBService.isProdStockLocked(prodId)) {
                log.info("------ 4 锁定商品库存成功，用户编号：{}，商品编号：{} ------", userId, prodId);
                break;
            }
        }

        // 模拟生成订单，省略实体参数
        String tradeOrderNo = WxpayUtil.generateOutTradeNo();

        log.info("----- 5 开始生成订单,用户:{},商品:{}，购买数量:{}，订单号:{} ------", userId, prodId, buyCount, tradeOrderNo);

        // 将当前订单信息存入redis
        rushBService.setTradeOrderTORedis(userId, prodId, tradeOrderNo);

        // 调用订单中心接口，生成订单,在订单接口信息
        rushBService.createBuyLimitTradeOrder(userId, prodId, buyCount, tradeOrderNo);

        // 等待订单生成成功
        while (true) {
            // 判断订单是否生成成功
            int createStatus = rushBService.getCreatedTradeOrderStatus(userId, prodId);
            if (createStatus == 1) {
                log.info("----- 6 订单生成成功,用户:{},商品:{}，购买数量:{}，订单号:{} ------", userId, prodId, buyCount, tradeOrderNo);
                break;
            } else if (createStatus == 2 || createStatus == 3) {
                // 订单生成失败或者异常
                log.warn("----- 7 订单生成失败,用户:{},商品:{}，购买数量:{}，订单号:{} ------", userId, prodId, buyCount, tradeOrderNo);
                // 释放锁住用户
                rushBService.unlockBuyLimitUser(userId, prodId);

                // 释放库存锁（不用扣减库存）
                rushBService.unlockProdStock(prodId);
                return ResultBuildUtil.fail("5005", "抢购失败，请重新操作！");
            }
        }

        // 订单生成成功，扣减库存
        rushBService.subProdStockNum(prodId, buyCount);
        log.info("订单生成成功，扣减库存");

        // 释放库存锁
        rushBService.unlockProdStock(prodId);
        log.info("释放库存锁");

        log.info("------ 抢购成功，用户编号：{}，商品编号：{}，购买数量：{} ------", userId, prodId, buyCount);

        RushBFormat rb = new RushBFormat();
        rb.setTip("抢购成功!");
        rb.setTradeOrderNo(tradeOrderNo);
        return ResultBuildUtil.success(rb);
    }

    /**
     * @author : Lcywings
     * @date : 2021/8/12 11:21
     * @acl : true
     * @description : 预约抢购入口
     */
    @ApiImplicitParams({
            @ApiImplicitParam(name = "productCode", required = true, defaultValue = "0", value = "商品编码")
    })
    @ApiResponses({
            @ApiResponse(code = 4002, message = "用户未登录"),
            @ApiResponse(code = 4003, message = "不存在的商品编号"),
            @ApiResponse(code = 6001, message = "预约失败！不是抢购商品。"),
            @ApiResponse(code = 4003, message = "不存在的商品编号"),
    })
    @ApiOperation(value = "预约抢购入口", notes = "预约后到点提醒，再次点击取消预约")
    @GetMapping(value = "/appointment")
    public RequestResult<String> appointment(HttpServletRequest request, @RequestParam String productCode) {
        // 校验数据
        Integer userId = getUserIdByToken(request);
        Integer prodId = getProductIdByproductCode(productCode);
        if (userId == -1) {
            return ResultBuildUtil.fail("4002", "用户未登录");
        }
        if (prodId == -1) {
            return ResultBuildUtil.fail("4003", "不存在的商品编号");
        }

        log.info("判断是否是抢购商品");
        if (!rushBService.isM2Product(prodId)) {
            return ResultBuildUtil.fail("6001", "预约失败！不是抢购商品。");
        }

        log.info("判断抢购商品是否不在预约时间");
        if (!rushBService.isM2ProductBuyTime(prodId)) {
            return ResultBuildUtil.fail("6002", "预约失败！不在预约时间，无法预约商品。");
        }


        log.info("判断是否是已经预约");
        if (!rushBService.appointment(userId, prodId)) {
            return ResultBuildUtil.success("已经取消预约改商品！");
        }

        return ResultBuildUtil.success("预约成功！");
    }

    private Integer getUserIdByToken(HttpServletRequest request) {
        String token = request.getHeader("token");
        Integer userId = searchService.selectUserIdByToken(token);
        log.info("userId：{}", userId);
        return userId;
    }

    //根据productCode拿到productId
    private Integer getProductIdByproductCode(String productCode) {
        Integer productId = searchService.getProductIdByproductCode(productCode);
        log.info("查到的productId是：{}", productId);
        return productId;
    }
}
